﻿using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;

using FarseerPhysics.Collision;
using FarseerPhysics.Common;
using System;
using Microsoft.Xna.Framework.Net;

namespace Spacer {
	public class GalaxySession : Activity, IDisposable {
		public GalaxySession() {}

		public GalaxySession( Galaxy galaxy ) {
			Galaxy = galaxy;
		}

		public void Initialize() {
			TextureCircle = Main.FindTexture( "SmallCircle" );
			SpriteSelection = Main.Sprites["Shield"];

			// Create new galaxy
			if( Galaxy == null ) {
				Galaxy = new Galaxy( typeof(GalaxyGenerator) );
				//Globals.Galaxy.Name = Helper.GetStringInput( "Name your galaxy!", "You can enter a name for your galaxy to save multiple.", "Galaxy" );
			}

			RenderedNodes = new HashSet<Tree<SolarSys>>();
			SelectedNode = Galaxy.SystemsGraph;
			DrawOffset = SelectedNode.Data.Position;
			DrawScale = 10f;
			DrawRotation = 0f;

			if( Globals.GamerServicesEnabled ) {
				Network.CreateSession();
				//if( Gamer.SignedInGamers.Count > 0 ) {
				//	OnPlayerJoin( Network.CurrentSession.LocalGamers[0] );
				//}
			} else {
				OnPlayerJoin( null );
			}
		}

		public void Shutdown() {
			// Save it
			//Helper.DeleteDir( "Saves" );
			//Helper.CreateDir( "Saves" );
			//Helper.CreateDir( "Saves\\Galaxy" );
			//Helper.CreateDir( "Saves\\Galaxy\\Players" );

			Globals.PushActivity( new PleaseWait(
				() => {
					Galaxy.ClearLoadCache();
					XmlObjectCfg<Galaxy>.Save( Galaxy, "Saves\\Galaxy\\Galaxy.xml" );
			
					PlayerController[] controllers = new PlayerController[PlayerController.Players.Count];
					PlayerController.Players.CopyTo( controllers );
					foreach(PlayerController controller in controllers)
						OnPlayerLeave( controller );

					return null;
				},
				obj => {
					Network.DestroySession();
				}
			) );

			/*Galaxy.ClearLoadCache();
			XmlObjectCfg<Galaxy>.Save( Galaxy, "Saves\\Galaxy\\Galaxy.xml" );
			
			PlayerController[] controllers = new PlayerController[PlayerController.Players.Count];
			PlayerController.Players.CopyTo( controllers );
			foreach(PlayerController controller in controllers)
				OnPlayerLeave( controller );

			Network.DestroySession();*/
		}
		  
		public void Resumed() {
			SelectedNode = (Globals.LocalPlayer.LastSystem != null ? Globals.LocalPlayer.LastSystem.GalaxyNode : Galaxy.SystemsGraph);
			DrawOffset = SelectedNode.Data.Position;
		}

		public void Paused() {
		}

		public void Update() {
			if( Globals.GameTime >= LastSelection+0.5f ) {
				Vector2 left_thumb = Input.GetUserCmd( PlayerIndex.One ).LeftThumb;
				left_thumb.Y *= -1;

				if( left_thumb.Length() > 0.8f ) { 
					float closest_dot = -1f;
					Tree<SolarSys> closest_node = null;

					foreach(Tree<SolarSys> child in SelectedNode.Children) {
						Vector2 norm = child.Data.Position - SelectedNode.Data.Position;
						norm.Normalize();
						norm = Vector2.Transform( norm, Matrix.CreateRotationZ(DrawRotation) );

						float dot = Vector2.Dot( left_thumb, norm );
						if( dot > 0.5f && (dot > closest_dot || closest_node == null) ) {
							closest_dot = dot;
							closest_node = child;
						}
					}

					if( closest_node != null ) {
						SelectedNode = closest_node;
						LastSelection = Globals.GameTime;
					}
				}
			}

			if( Globals.CurrentActivity == this && ((Input.IsKeyDown(Keys.Enter) && !Input.WasKeyDown(Keys.Enter)) ||
				(Input.IsGamePadButtonDown(GamePadButton.A) && !Input.WasGamePadButtonDown(GamePadButton.A))) ) {

				Tree<SolarSys> player_system = (Globals.LocalPlayer.LastSystem != null ? Globals.LocalPlayer.LastSystem.GalaxyNode : Galaxy.SystemsGraph);

				if( SelectedNode != null && SelectedNode.Data != null && (SelectedNode == player_system || player_system.Children.Contains(SelectedNode)) ) {
					SelectedNode.Data.OnPlayerJoin( Globals.LocalPlayer );
					Globals.PushActivity( SelectedNode.Data );
				}
			}

			if( Globals.CurrentActivity == this && ((Input.IsKeyDown(Keys.Escape) && !Input.WasKeyDown(Keys.Escape)) ||
				(Input.IsGamePadButtonDown(GamePadButton.Back) && !Input.WasGamePadButtonDown(GamePadButton.Back))) ) {
				Finished = true;
			}
		}

		public void UpdateSolarSystems() {
			HashSet<SolarSys> active = new HashSet<SolarSys>();

			// Get active solar systems
			foreach(PlayerController controller in PlayerController.Players) {
				if( controller.CurrentSystem != null && controller.CurrentSystem.Galaxy == Galaxy )
					active.Add( controller.CurrentSystem );
			}

			// Update the currently active solar systems
			foreach(SolarSys sys in active) {
				sys.InternalUpdate();
			}
		}

		public void DrawWorld() {
		}

		public void DrawOverlay() {
			if( Input.IsKeyDown(Keys.Up) || Input.IsGamePadButtonDown(GamePadButton.RightBumper) )
				DrawScale = Math.Max( 5, DrawScale + 3 * Globals.DrawDeltaTime );
			if( Input.IsKeyDown(Keys.Down) || Input.IsGamePadButtonDown(GamePadButton.LeftBumper) )
				DrawScale = Math.Min( 15, DrawScale - 3 * Globals.DrawDeltaTime );
			if( Input.IsKeyDown(Keys.Left) || Input.GetUserCmd(PlayerIndex.One).LeftTrigger > 0.3f )
				DrawRotation = DrawRotation - 0.6f * Globals.DrawDeltaTime;
			if( Input.IsKeyDown(Keys.Right) || Input.GetUserCmd(PlayerIndex.One).RightTrigger > 0.3f )
				DrawRotation = DrawRotation + 0.5f * Globals.DrawDeltaTime;

			DrawRotation += 0.1f * Globals.DrawDeltaTime;

			Count = 0;
			Left = 0;
			Right = 0;
			Top = 0;
			Bottom = 0;

			RenderedNodes.Clear();

			foreach(SolarSys system in Galaxy.Systems.Values) {
				DrawNode( system.GalaxyNode );
			}

			Surface.DrawColor = Color.White;
			Surface.DrawString( "Default", "Seed: "+Galaxy.Seed, new Vector2(5f,0f), Vector2.Zero, 0f );
			Surface.DrawString( "Default", "Solar Systems: "+Count, new Vector2(5f,30f), Vector2.Zero, 0f );
			Surface.DrawString( "Default", "Size: "+(Right-Left)+", "+(Bottom-Top), new Vector2(5f,60f), Vector2.Zero, 0f );
		} 

		public bool IsModal() {
			return false;
		}

		public bool IsTransparent() {
			return false;
		}

		public bool IsFinished() {
			return Finished;
		}

		// ----------

		public void OnPlayerJoin( NetworkGamer gamer ) {
			if( !IsHost )
				return;

			PlayerController controller;

			string name = (gamer != null ? gamer.Gamertag : "Player");
			if( Helper.ExistsFile("Saves\\Galaxy\\Players\\"+name+".xml") ) {
				controller = XmlObjectCfg<PlayerController>.Load( "Saves\\Galaxy\\Players\\"+name+".xml" );
				if( gamer == null || gamer.IsLocal ) {
					controller.ControllingGamer = gamer;
					controller.LastSystem = Galaxy.Systems[controller.CurrentSystemID];

					Globals.LocalPlayer = controller;
					SelectedNode = (Globals.LocalPlayer.LastSystem != null ? Globals.LocalPlayer.LastSystem.GalaxyNode : Galaxy.SystemsGraph);
				}
			} else {
                if ((gamer == null || gamer.IsLocal) && Globals.LocalPlayer == null )
					Globals.LocalPlayer = controller = new PlayerController( gamer );
				else
					controller = new PlayerController( gamer );
			}
			
			if( controller.CurrentShipType == null ) {
				Globals.PushActivity( new ShipSelectionScreen(controller) );
			}
		}

		public void OnPlayerLeave( PlayerController controller ) {
			if( !IsHost )
				return;

			XmlObjectCfg<PlayerController>.Save( controller, "Saves\\Galaxy\\Players\\"+controller.ControllingGamerName+".xml" );
			PlayerController.RemoveController( controller );
		}

		public void OnPlayerLeave( NetworkGamer gamer ) {
			if( !IsHost )
				return;

			PlayerController controller = null;
			foreach(PlayerController c in PlayerController.Players) {
				if( c.ControllingGamer == gamer ) {
					controller = c;
					break;
				}
			}

			if( controller != null ) {
				XmlObjectCfg<PlayerController>.Save( controller, "Saves\\Galaxy\\Players\\"+controller.ControllingGamerName+".xml" );
				PlayerController.RemoveController( controller );
			}
		}

		// ----------

		public void DrawNode( Tree<SolarSys> node ) {
			bool current_location = false;
			bool connected_to_current_location = false;
			
			Tree<SolarSys> player_system = (Globals.LocalPlayer.LastSystem != null ? Globals.LocalPlayer.LastSystem.GalaxyNode : Galaxy.SystemsGraph);

			if( player_system.Data == node.Data )
				current_location = true;
			
			if( node.Children.Contains(player_system) )
				connected_to_current_location = true;

			Vector2 center = new Vector2( Surface.Width*0.5f, Surface.Height*0.5f );

			// Approach selected node
			Vector2 dir = DrawOffset - SelectedNode.Data.Position;
			float dist = dir.Length();
			dist *= 1f - 0.1f * Globals.DrawDeltaTime;
			if( dist > 0 ) {
				dir.Normalize();
				DrawOffset = SelectedNode.Data.Position + dist * dir;
			}

			Matrix scale = Matrix.CreateTranslation( -DrawOffset.X, -DrawOffset.Y, 0f ) * Matrix.CreateScale( DrawScale ) * Matrix.CreateRotationZ( DrawRotation );
			Vector2 pos1 = node.Data.Position;
			pos1 = Vector2.Transform( pos1, scale );

			// Draw connections
			if( current_location )
				Surface.DrawColor = new Color( 20, 20, 200 );
			else
				Surface.DrawColor = new Color( 200, 20, 20 );

			foreach(Tree<SolarSys> child in node.Children) {
				if( (node != player_system && !RenderedNodes.Contains(child)) || child == player_system )
					continue;

				Vector2 pos2 = child.Data.Position;
				pos2 = Vector2.Transform( pos2, scale );

				Surface.DrawLine( center+pos1, center+pos2, (current_location ? 0.1f : 0.05f)*DrawScale, 1f );
			}

			// Draw selection
			if( node == SelectedNode ) {
				if( current_location || connected_to_current_location )
					Surface.DrawColor = new Color( 50, 255, 50 );
				else
					Surface.DrawColor = new Color( 200, 200, 200 );

				Surface.DrawSprite( SpriteSelection, center+pos1, new Vector2(DrawScale/2,DrawScale/2)*2, new Vector2(0,0), 0f, 0 );
			}

			// Draw node
			if( current_location )
				Surface.DrawColor = new Color( 50, 255, 50 );
			else if( connected_to_current_location )
				Surface.DrawColor = new Color( 50, 50, 255 );
			else
				Surface.DrawColor = new Color( 255, 50, 50 );

			Surface.DrawRect( TextureCircle, center+pos1-new Vector2(DrawScale/4,DrawScale/4), new Vector2(DrawScale/2,DrawScale/2), new Vector2(0,0), 0f );
			
			// Extra stuff
			Count++;
			Left = (node.Data.Position.X < Left ? node.Data.Position.X : Left);
			Right = (node.Data.Position.X > Right ? node.Data.Position.X : Right);
			Top = (node.Data.Position.Y < Top ? node.Data.Position.Y : Top);
			Bottom = (node.Data.Position.Y > Bottom ? node.Data.Position.Y : Bottom);

			RenderedNodes.Add( node );
		}

		// ----------

		public Galaxy Galaxy { get; private set; }
		public bool IsHost { get; set; }

		private Texture2D TextureCircle { get; set; }
		private Sprite SpriteSelection { get; set; }
		private Vector2 DrawOffset { get; set; }
		private float DrawScale { get; set; }
		private float DrawRotation { get; set; }
		public bool Finished { get; set; }

		private HashSet<Tree<SolarSys>> RenderedNodes { get; set; }
		private Tree<SolarSys> SelectedNode { get; set; }
		private float LastSelection { get; set; }

		private int Count { get; set; }
		private float Left { get; set; }
		private float Right { get; set; }
		private float Top { get; set; }
		private float Bottom { get; set; }

		// ----------

		public void Dispose() {
			foreach(SolarSys sys in Galaxy.Systems.Values) {
				sys.GalaxyNode.Children.Clear();
			}
			Galaxy.Systems.Clear();

			RenderedNodes.Clear();
		}
	}
}
